package com.db.hibernate;

import java.util.Properties;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;

import com.exception.DataAccessException;



public class HibernateUtil {

    private static final Logger logger = Logger.getLogger(HibernateUtil.class);

    private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";

    /** Holds a single instance of Session */
    private static final ThreadLocal threadLocalSession = new ThreadLocal();

    /** Holds a single instance of Transaction */
    private static final ThreadLocal threadLocalTransaction = new ThreadLocal();

    /** The single instance of hibernate SessionFactory */
    private static SessionFactory sessionFactory;

    /*private static int counterForIn = 0;
    
    private static int counterForOut = 0;
    
    private static int sessionCount = 0;
    */
     
    //public static Hashtable ht = new Hashtable();
    
     
	/** Constructor */
	private HibernateUtil() {
    }
    
    
    public static void setSessionForTest(Session session) {
        threadLocalSession.set(session);
    }

    public static Session getSession() throws DataAccessException{

        Session session = (Session) threadLocalSession.get();

        if (session == null) {
            try {
                if (sessionFactory == null) {
                    Configuration cfg = new Configuration();
                    sessionFactory = cfg.configure().buildSessionFactory();
                }
                session = sessionFactory.openSession();

                //    changes for SAGPROF-86 end 
             //   logger.error("Connection Test: Count:" + " Open: " + "HibernateUtil: " + "Thread Id:" + Thread.currentThread().getId() + "Thread Name: " + Thread.currentThread().getName() + "session id: " + session.hashCode());
               // System.out.println(("Connection Test: Count:" + " Open: " + "HibernateUtil: " + "Thread Id:" + Thread.currentThread().getId() + "Thread Name: " + Thread.currentThread().getName() + "session id: " + session.hashCode()));
               // ht.put(session, Thread.currentThread().getStackTrace());
                //session.connection().setAutoCommit(false);
                
                /*
				 * Changes made to get the database connection
				 * as session.connection() is DEPRECATED in Hibernate3. 
				 */							
				/* ((SessionFactoryImplementor)sessionFactory).
				 				getConnectionProvider().getConnection().setAutoCommit(false);*/
				
                threadLocalSession.set(session);
            } catch (Throwable t) {
				throw new DataAccessException(t);
            }
        }
        return session;
    }

    public static void closeSession() throws DataAccessException{
        Session session = (Session) threadLocalSession.get();

        threadLocalSession.set(null);
        if (session != null && session.isOpen()) {
            try {
            	/**
            	 * This code is used for testing the number of connection in  application. while opening the connection
            	 *  all the values are stored in Hashtable and in closed() method that value is removed.
            	 */
            	
            /*	logger.error("Connection Test: Count: " + " Close: " + "HibernateUtil: " + "Thread Id: " + Thread.currentThread().getId() + "Thread Name: " + Thread.currentThread().getName() + "session id: " + session.hashCode());
            	//System.out.println("Connection Test: Count: " + " Close: " + "HibernateUtil: " + "Thread Id: " + Thread.currentThread().getId() + "Thread Name: " + Thread.currentThread().getName() + "session id: " + session.hashCode());
            	 if(ht.containsKey(session)){
                 	ht.remove(session);
             	}
                 else
                 {
                 	logger.error("Connection Test: Count: " + " Mismatch: " + "HibernateUtil: " + "Thread Id: " + Thread.currentThread().getId() + "Thread Name: " + Thread.currentThread().getName() + "session id: " + session.hashCode());
                 	logger.error("Connection Test: HibernateUtil:" + Thread.currentThread().getStackTrace());
                 }*/
            	 
            	//session.flush();
                session.close();
                
/*                Set set = ht.keySet();
            	for(Object sess : set ) {
            		logger.error("Number of open connections:" + sess.hashCode() + "stacktrace:" + Thread.currentThread().getStackTrace().toString());
            		System.out.println("Number of open connections:" + sess.hashCode() + "stacktrace:" + Thread.currentThread().getStackTrace().toString());
            	}
*/            } catch (HibernateException e) {
            	throw new DataAccessException(e);
            }
        }
    }

    public static void flush() throws DataAccessException{
        Session session = (Session) threadLocalSession.get();

        threadLocalSession.set(null);
        if (session != null && session.isOpen()) {
            try {
                session.flush();
            } catch (HibernateException e) {
            	throw new DataAccessException(e);
            }
        }
    }
    
    public static void beginTransaction() throws DataAccessException{
        Transaction tx = (Transaction) threadLocalTransaction.get();
        try {
            if (tx == null) {
                tx = getSession().beginTransaction();
                threadLocalTransaction.set(tx);
            }
        } catch(HibernateException e) {
        	throw new DataAccessException(e);
		}
    }


    public static void commitTransaction() throws DataAccessException{
        Transaction tx = (Transaction) threadLocalTransaction.get();
        try {
	        if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
	            tx.commit();
	            threadLocalTransaction.set(null);
	        }
        } catch (HibernateException he) {
        	throw new DataAccessException(he);
        }
    }

    public static void rollbackTransaction() throws DataAccessException{
        Transaction tx = (Transaction) threadLocalTransaction.get();
        try {
            if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
                tx.rollback();
                threadLocalTransaction.set(null);
            }
        } catch(HibernateException e) {
        	throw new DataAccessException(e);
        }
    }

	public static Query createQuery(String queryString) throws DataAccessException{
			
		try {
			Query query = getSession().createQuery(queryString);
			query.setCacheable(false);
			return query; 
		} catch (HibernateException e) {
			throw new DataAccessException(e);
		}
	}  

    /**
     * Different database store/treat the null value in the database differently. 
     * Some databases like oracle save the null String value as null. Some 
     * databases like SQL Server save the null String value as '' - empty string.   
     * <p>This method checks the dialect class name. If the dialect is for the 
     * database that returns null String values as null then this method return 
     * true and false otherwise.
     * 
     * @return true if database treats null for null String & false if it's trated as ''
     * 
     * @author abhishek_chaudhari
     */
    public static boolean isEmptyStringNull() {
        Properties props = Environment.getProperties();
        
        String dialect = props.getProperty(Environment.DIALECT);
        
        if (dialect.indexOf("Oracle") != -1 ) {
            return true;
        }
        
        return false;
    }
    
    /**
     * This method returns the not null check HQL snippet based on the database
     * used. Some databases like oracle save the null String value as null. Some 
     * databases like SQL Server save the null String value as '' - empty string.
     * <p> Hence, for oracle it'll return " is not null " and for SQL server 
     * it'll return " != '' "  
     * 
     * @return HQL snippet for not null check of string
     * 
     * @author abhishek_chaudhari
     */
    public static String stringNotNullCheckHQL() {
        
        if (isEmptyStringNull()) {
            return " is not null ";
        }
        
        return " != '' ";
    }

    /**
     * This method returns the null check HQL snippet based on the database
     * used. Some databases like oracle save the null String value as null. Some 
     * databases like SQL Server save the null String value as '' - empty string.
     * <p> Hence, for oracle it'll return " is null " and for SQL server it'll 
     * return " = '' "
     * 
     * @return HQL snippet for null check of string
     * 
     * @author abhishek_chaudhari
     */
    public static String stringNullCheckHQL() {
        
        if (isEmptyStringNull()) {
            return " is null ";
        }
        
        return " = '' ";
    }
    
    
}
